/**************************************************************************
 //* W Corp. 
 //* W Profile Services                                             
 //* Package:  by.doubleyou.monitor.peer
 //* (C) Copyright W Corp.  2007
 /**************************************************************************
 *
 * Copyright (c) 2007, W Corporation
 * All rights reserved.
 * 
 * Version: 1.0
 * Date:  3 Dec, 2007, Author: Alexander J. Perez Tchernov
 * 
 * MODIFICATION HISTORY:
 * Defect/Feature  	Description
 * ---
 */
package by.doubleyou.core.gms;

import static java.lang.Thread.sleep;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.enterprise.ee.cms.core.CallBack;
import com.sun.enterprise.ee.cms.core.FailureNotificationSignal;
import com.sun.enterprise.ee.cms.core.FailureRecoverySignal;
import com.sun.enterprise.ee.cms.core.FailureSuspectedSignal;
import com.sun.enterprise.ee.cms.core.GMSException;
import com.sun.enterprise.ee.cms.core.GMSFactory;
import com.sun.enterprise.ee.cms.core.GroupHandle;
import com.sun.enterprise.ee.cms.core.GroupManagementService;
import com.sun.enterprise.ee.cms.core.JoinNotificationSignal;
import com.sun.enterprise.ee.cms.core.MessageSignal;
import com.sun.enterprise.ee.cms.core.PlannedShutdownSignal;
import com.sun.enterprise.ee.cms.core.Signal;
import com.sun.enterprise.ee.cms.core.SignalAcquireException;
import com.sun.enterprise.ee.cms.core.SignalReleaseException;
import com.sun.enterprise.ee.cms.impl.client.FailureNotificationActionFactoryImpl;
import com.sun.enterprise.ee.cms.impl.client.FailureRecoveryActionFactoryImpl;
import com.sun.enterprise.ee.cms.impl.client.JoinNotificationActionFactoryImpl;
import com.sun.enterprise.ee.cms.impl.client.JoinedAndReadyNotificationActionFactoryImpl;
import com.sun.enterprise.ee.cms.impl.client.MessageActionFactoryImpl;
import com.sun.enterprise.ee.cms.impl.client.PlannedShutdownActionFactoryImpl;
import com.sun.enterprise.ee.cms.logging.GMSLogDomain;

/**
 * GMS Service
 * 
 * @author Alexander J. Perez Tchernov
 * 
 */
public class GMSClientService implements Runnable, CallBack {
	private GroupManagementService gms;

	private Logger logger = GMSLogDomain.getLogger(GMSLogDomain.GMS_LOGGER);
	 
	private Thread flag;

	private String memberToken;

	private boolean sendMessages;

	private String serviceName;

	private static final int MILLIS = 4000;

	public static final String IIOP_MEMBER_DETAILS_KEY = "IIOPListenerEndPoints";

	/**
	 * Constructor
	 * 
	 * @param serviceName
	 * @param memberToken
	 *            instanceId
	 * @param sendMessages
	 *            if messaging is allowed for the service
	 */
	public GMSClientService(final String serviceName, final String memberToken,
			final boolean sendMessages) {
		this.sendMessages = sendMessages;
		this.serviceName = serviceName;
		this.memberToken = memberToken;
		try {
			gms = GMSFactory.getGMSModule();
		} catch (GMSException e) {
			logger.log(Level.WARNING, e.getLocalizedMessage());
		}
		logger.setLevel(Level.INFO);
		registerFactories(gms);

	}
	
	/**
	 * Logger
	 * 
	 * @return
	 */
	protected Logger getLogger(){
		return logger;
	}

	/**
	 * Template methods to register for appropriate callbacks
	 * 
	 * @param gms
	 */
	protected void registerFactories(GroupManagementService gms) {
		// when override you may to omit registration of unnecessary factories
		gms.addActionFactory(new PlannedShutdownActionFactoryImpl(this));
		gms.addActionFactory(new JoinNotificationActionFactoryImpl(this));
		gms.addActionFactory(new FailureNotificationActionFactoryImpl(this));
		gms.addActionFactory(serviceName, new FailureRecoveryActionFactoryImpl(
				this));
		gms.addActionFactory(new MessageActionFactoryImpl(this), serviceName);
		gms.addActionFactory(new JoinedAndReadyNotificationActionFactoryImpl(
				this));
	}

	/**
	 * Start Client Service
	 * 
	 */
	public void start() {
		flag = new Thread(this, "GMSClient:" + serviceName);
		flag.start();
	}

	/**
	 * Stop Client Service
	 * 
	 */
	public void stopClient() {
		flag = null;
	}
	/**
	 * Get GMS
	 * 
	 * @return
	 */
	protected GroupManagementService getGMS(){
		return gms;
	}
	
	protected boolean isStopped(){
		return flag == Thread.currentThread(); //TODO make final
	}

	public boolean isSendMessage() {
		return sendMessages;
	}

	public void run() {
		GroupHandle gh = gms.getGroupHandle();
		// locked
		while ((gh.isFenced(serviceName, memberToken))) {
			try {
				logger.log(Level.FINEST, "Waiting for fence to be lowered");
				sleep(2000);
			} catch (InterruptedException e) {
				logger.log(Level.WARNING, "Thread interrupted:"
						+ e.getLocalizedMessage());
			}
		}
		logger.log(Level.INFO, serviceName + ":" + memberToken
				+ ": is not fenced now, starting GMSclient:" + serviceName);
		logger.log(Level.INFO, "DUMPING:"
				+ gms.getAllMemberDetails(IIOP_MEMBER_DETAILS_KEY));

		
	}

	public synchronized void processNotification(final Signal notification) {
		final String serverToken;		
		logger.log(Level.FINEST, new StringBuffer().append(serviceName).append(
				": Notification Received from:").append(
				notification.getMemberToken()).append(":[").append(
				notification.toString()).append("] has been processed")
				.toString());
		if (notification instanceof FailureRecoverySignal) {
			try {
				notification.acquire();
				extractMemberDetails(notification, notification
						.getMemberToken());
				sleep(MILLIS);
				notification.release();
			} catch (SignalAcquireException e) {
				logger.log(Level.WARNING, e.getLocalizedMessage());
			} catch (SignalReleaseException e) {
				logger.log(Level.INFO, e.getLocalizedMessage());
			} catch (InterruptedException e) {
				logger.log(Level.INFO, e.getLocalizedMessage());
			}
		} else if (notification instanceof JoinNotificationSignal
				|| notification instanceof FailureNotificationSignal
				|| notification instanceof PlannedShutdownSignal
				|| notification instanceof FailureSuspectedSignal) {
			serverToken = notification.getMemberToken();
			extractMemberDetails(notification, serverToken);

		} else if (notification instanceof MessageSignal) {
			serverToken = notification.getMemberToken();
			extractMemberDetails(notification, serverToken);
			processMessage((MessageSignal) notification);
		}
	}

	// ---------------- CUSTOM METHODS --------------------
	// TODO do the gof.template_methods or super. invocation
	protected void processMessage(MessageSignal signal) {
		// need to be overriden
	}

	// ---------------- / CUSTOM METHODS --------------------

	/**
	 * Get Member Token
	 * 
	 * @return
	 */
	public String getMemberToken() {
		return memberToken;
	}

	/**
	 * Get Service Name
	 * 
	 * @return
	 */
	public String getServiceName() {
		return serviceName;
	}

	/*
	 * Utility method
	 */
	private void extractMemberDetails(final Signal notification,
			final String serverToken) {
		logger
				.log(Level.FINEST, serviceName
						+ ":Now getting member details...");
		final Map memberDetails = notification.getMemberDetails();
		if (memberDetails.size() == 0) {
			logger.log(Level.FINEST, "No Details available");
		} else {
			logger.log(Level.FINEST, memberDetails.toString());
			for (Object key : memberDetails.keySet()) {
				logger.log(Level.FINEST, new StringBuffer().append(
						"Got Member Details for ").append(serverToken)
						.toString());
				logger.log(Level.FINEST, new StringBuffer().append("Key:")
						.append(key).append(":Value:").append(
								memberDetails.get(key).toString()).toString());
			}
		}
	}

}